package entity;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import javax.persistence.Embeddable;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import exception.AlmiranteException;

import util.DateUtil;

@Embeddable
public class Periodo {
	@Temporal(TemporalType.DATE)
	private Date inicio;
	
	@Temporal(TemporalType.DATE)
	private Date fim;

	/** Para uso do hibernate. */
	Periodo() {}
	
	public Periodo(Date inicio, Date fim) {
		if (inicio == null)
			throw new AlmiranteException("Informe o início do período");
		
		this.inicio = DateUtil.somenteData(inicio);
		
		if (fim != null) {
			this.fim = DateUtil.somenteData(fim);
		} else {
			this.fim = this.inicio;
		}
		if (this.inicio.after(this.fim))
			throw new AlmiranteException("Início do período deve ser anterior ao fim!");
	}
	
	public Periodo(Date inicio, int intervalo) {
		this(inicio, null);
		if (intervalo <= 0)
			throw new AlmiranteException("Intervalo precisa ser maior que 0");
		
		Calendar calFim = new GregorianCalendar();
		calFim.setTime(inicio);
		calFim.add(Calendar.DAY_OF_MONTH, intervalo - 1);
		this.fim = DateUtil.somenteData(calFim.getTime());
	}

	public int intervaloEmDias() {
		return DateUtil.diferencaEmDias(inicio, fim) + 1;
	}

	public Periodo novoInicio(Date inicio) {
		return new Periodo(inicio, intervaloEmDias());
	}
	
	public Date getInicio() {
		return inicio;
	}
	
	public Periodo novoFim(Date fim) {
		if (fim.before(this.inicio))
			throw new AlmiranteException("Fim do período deve anterior ao início");
		
		return new Periodo(this.inicio, fim);
	}

	public Date getFim() {
		return fim;
	}
	
	public Periodo novoIntervalo(int intervalo) {
		return new Periodo(this.inicio, intervalo);
	}

	public boolean contem(Periodo periodo) {
		boolean inicioAntesOuIgual = this.inicio.before(periodo.getInicio()) || this.inicio.equals(periodo.getInicio());
		boolean fimDepoisOuIgual = this.fim.after(periodo.getFim()) || this.fim.equals(periodo.getFim());
		
		return inicioAntesOuIgual && fimDepoisOuIgual;
	}
	
	public boolean contem(Date data) {
		return data.equals(inicio) || data.equals(fim) || (data.after(inicio) && data.before(fim));
	}

	public boolean antesDe(Periodo periodo) {
		return this.fim.before(periodo.inicio);
	}

	public boolean depoisDe(Periodo periodo) {
		return this.inicio.after(periodo.fim);
	}
	
	public boolean concorrenteA(Periodo periodo) {
		return contem(periodo.inicio) || contem(periodo.fim);
	}
	
	public Periodo novoPeriodoPosterior(int intervalo) {
		Calendar inicioNovoPeriodo = new GregorianCalendar();
		inicioNovoPeriodo.setTime(fim);
		inicioNovoPeriodo.add(Calendar.DATE, 1);
		
		return new Periodo(inicioNovoPeriodo.getTime(), intervalo);
	}
	
	public Periodo adiantarEmDias(int dias) {
		Calendar novoInicio = new GregorianCalendar();
		novoInicio.setTime(getInicio());
		novoInicio.add(Calendar.DATE, dias);
		
		return novoInicio(novoInicio.getTime());
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((fim == null) ? 0 : fim.hashCode());
		result = prime * result + ((inicio == null) ? 0 : inicio.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Periodo other = (Periodo) obj;
		if (fim == null) {
			if (other.fim != null)
				return false;
		} else if (!fim.equals(other.fim))
			return false;
		if (inicio == null) {
			if (other.inicio != null)
				return false;
		} else if (!inicio.equals(other.inicio))
			return false;
		return true;
	}
}
